﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Web.Mvc;

namespace BOSS.Web.Extensions
{
    /// <summary>
    /// 扩展辅助类
    /// </summary>
    internal static class ExtensionHelper
    {
        /// <summary>
        /// 属性表达式
        /// </summary>
        private static readonly Regex PropertyExpressionRegex = new Regex(@"(?<PropName>\w+)(\[(?<PropIndex>.+)\])?(\.(?<ChildProp>.+))*", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);

        /// <summary>
        /// 获取属性
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="name"></param>
        /// <param name="propertyValue"></param>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        internal static Type RetrieveProperty(HtmlHelper htmlHelper, string name, out object propertyValue, out PropertyInfo propertyInfo)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("参数不应为空", "name");
            }

            propertyInfo = null;
            propertyValue = null;

            Type type = null;
            object value = htmlHelper.ViewData.Model;
            PropertyInfo info = null;

            // 优先在模型中进行解析
            if (value != null)
            {
                type = EvalProperty(name, ref value, out info);
            }

            // 如果不在模型中，则通过索引在视图数据中解析
            if (type == null)
            {
                //if (strArray.Length < 2)
                //{
                //    // 未找到 name 所对应的属性
                //    return null;
                //}

                int dotIndex = name.IndexOf('.');
                if (dotIndex == -1)
                {
                    value = htmlHelper.ViewData[name];
                    if (value == null)
                    {
                        // 未找到 name 所对应的属性
                        return null;
                    }

                    type = value.GetType();
                }
                else
                {
                    value = htmlHelper.ViewData[name.Substring(0, dotIndex)];
                    if (value == null)
                    {
                        // 未找到 name 所对应的属性
                        return null;
                    }

                    if (dotIndex == name.Length - 1)
                    {
                        return null;
                    }

                    type = EvalProperty(name.Substring(dotIndex + 1), ref value, out info);

                    // 表达式错误，找不到任何相关属性
                    if (info == null)
                    {
                        return null;
                    }
                }
            }

            propertyValue = value;
            propertyInfo = info;
            return type;
        }

        /// <summary>
        /// 根据表达式，获取属性
        /// </summary>
        /// <param name="propertyExpression"></param>
        /// <param name="propertyValue"></param>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        internal static Type EvalProperty(string propertyExpression, ref object propertyValue, out PropertyInfo propertyInfo)
        {
            propertyInfo = null;

            if (propertyValue == null)
            {
                return null;
            }

            Match match = PropertyExpressionRegex.Match(propertyExpression);
            if (match.Success)
            {
                propertyInfo = propertyValue.GetType().GetProperty(match.Groups["PropName"].Value, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
                if (propertyInfo == null)
                {
                    return null;
                }

                Type propertyType = propertyInfo.PropertyType;
                propertyValue = propertyInfo.GetValue(propertyValue, null);

                if (match.Groups["PropIndex"].Success)
                {
                    if (propertyValue == null)
                    {
                        return null;
                    }

                    string key = match.Groups["PropIndex"].Value;

                    // 处理哈希字典
                    object indexedValue = null;
                    MethodInfo info = propertyInfo.PropertyType.GetMethod("ContainsKey", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string) }, null);
                    if ((info == null) || ((bool)info.Invoke(propertyValue, new object[] { key })))
                    {
                        PropertyInfo info2 = propertyInfo.PropertyType.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance, null, null, new Type[] { typeof(string) }, null);
                        if (info2 != null)
                        {
                            propertyInfo = info2;
                            propertyType = info2.PropertyType;
                            indexedValue = info2.GetValue(propertyValue, new object[] { key });
                        }
                        else
                        {
                            PropertyInfo info3 = propertyInfo.PropertyType.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance, null, null, new Type[] { typeof(object) }, null);
                            if (info3 != null)
                            {
                                propertyInfo = info3;
                                propertyType = info3.PropertyType;
                                indexedValue = info3.GetValue(propertyValue, new object[] { key });
                            }
                        }
                    }
                    if (indexedValue != null)
                    {
                        propertyValue = indexedValue;
                    }
                    else
                    {
                        // 普通数组
                        PropertyDescriptorCollection pdc = TypeDescriptor.GetConverter(propertyValue).GetProperties(propertyValue);
                        if (pdc != null)
                        {
                            PropertyDescriptor propDesc = pdc.Find("[" + key + "]", true);
                            if (propDesc != null)
                            {
                                propertyType = propDesc.PropertyType;
                                propertyValue = propDesc.GetValue(propertyValue);
                            }
                            else
                            {
                                propertyValue = null;
                            }
                        }
                        else
                        {
                            propertyValue = null;
                        }
                    }
                }

                if (match.Groups["ChildProp"].Success)
                {
                    if (propertyValue == null)
                    {
                        return null;
                    }
                    return EvalProperty(match.Groups["ChildProp"].Value, ref propertyValue, out propertyInfo);
                }

                return propertyType;
            }
            else
            {
                return null;
            }
        }
    }
}
